/*************************************************
	作者: Plane
	邮箱: 1785275942@qq.com	
	功能: 使用Async await语法驱动，运行在线程池中，可设置多线程或逻辑主线程回调任务

    //=================*=================\\
           教学官网：www.qiqiker.com
           官方微信服务号: qiqikertuts
           Plane老师微信: PlaneZhong
               ~~获取更多教学资讯~~
    \\=================*=================//
*************************************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PETimer {
    public class AsyncTimer : PETimer {
        class AsyncTaskPack {
            public int tid;
            public Action<int> cb;
            public AsyncTaskPack(int tid, Action<int> cb) {
                this.tid = tid;
                this.cb = cb;
            }
        }

        private bool setHandle;
        private readonly ConcurrentDictionary<int, AsyncTask> taskDic;
        private ConcurrentQueue<AsyncTaskPack> packQue;
        private const string tidLock = "AsyncTimer_tidLock";

        public AsyncTimer(bool setHandle) {
            taskDic = new ConcurrentDictionary<int, AsyncTask>();
            this.setHandle = setHandle;
            if(setHandle) {
                packQue = new ConcurrentQueue<AsyncTaskPack>();
            }
        }

        public override int AddTask(
            uint delay,
            Action<int> taskCB,
            Action<int> cancelCB,
            int count = 1) {
            int tid = GenerateTid();
            AsyncTask task = new AsyncTask(tid, delay, count, taskCB, cancelCB);

            // 修复竞态条件：先添加到字典，再启动任务
            if(taskDic.TryAdd(tid, task)) {
                RunTaskInPool(task);
                return tid;
            }
            else {
                WarnFunc?.Invoke($"key:{tid} already exist.");
                return -1;
            }
        }
        public override bool DeleteTask(int tid) {
            if(taskDic.TryRemove(tid, out AsyncTask task)) {
                LogFunc?.Invoke($"Remove tid:{tid} task in taskDic Succ.");

                task.cts?.Cancel();

                if(setHandle && task.cancelCB != null) {
                    packQue.Enqueue(new AsyncTaskPack(tid, task.cancelCB));
                }
                else {
                    task.cancelCB?.Invoke(tid);
                }
                return true;
            }
            else {
                WarnFunc?.Invoke($"Remove tid:{tid} task in taskDic failed, task not found.");
                return false;
            }
        }
        public override void Reset() {
            if(packQue != null && !packQue.IsEmpty) {
                WarnFunc?.Invoke("Call Queue is not Empty.");
            }

            // 取消所有任务
            foreach(var kvp in taskDic) {
                kvp.Value.cts?.Cancel();
            }

            taskDic.Clear();
            tid = 0;
        }

        public override bool HasTask(int tid) {
            return taskDic.ContainsKey(tid);
        }

        public override TaskInfo? GetTaskInfo(int tid) {
            if(taskDic.TryGetValue(tid, out AsyncTask task)) {
                return new TaskInfo {
                    tid = task.tid,
                    delay = task.delay,
                    remainingCount = task.count > 0 ? task.count : -1, // -1 表示无限循环
                    status = task.ct.IsCancellationRequested ? TaskStatus.Cancelled : TaskStatus.Running
                };
            }
            return null;
        }

        public override List<int> GetAllTaskIds() {
            return new List<int>(taskDic.Keys);
        }

        public override int GetTaskCount() {
            return taskDic.Count;
        }

        public void HandleTask() {
            while(packQue != null && packQue.Count > 0) {
                if(packQue.TryDequeue(out AsyncTaskPack pack)) {
                    pack.cb?.Invoke(pack.tid);
                }
                else {
                    WarnFunc?.Invoke($"packQue dequeue data failed.");
                }
            }
        }

        void RunTaskInPool(AsyncTask task) {
            Task.Run(async () => {
                if(task.count > 0) {
                    do {
                        //限次数循环任务
                        --task.count;
                        ++task.loopIndex;
                        int delay = (int)(task.delay + task.fixDelta);
                        if(delay > 0) {
                            await Task.Delay(delay, task.ct);
                        }
                        TimeSpan ts = DateTime.UtcNow - task.startTime;
                        task.fixDelta = (int)(task.delay * task.loopIndex - ts.TotalMilliseconds);
                        CallBackTaskCB(task);
                    } while(task.count > 0);
                }
                else {
                    //永久循环任务
                    while(true) {
                        //限次数循环任务
                        ++task.loopIndex;
                        int delay = (int)(task.delay + task.fixDelta);
                        if(delay > 0) {
                            await Task.Delay(delay, task.ct);
                        }
                        TimeSpan ts = DateTime.UtcNow - task.startTime;
                        task.fixDelta = (int)(task.delay * task.loopIndex - ts.TotalMilliseconds);
                        CallBackTaskCB(task);
                    }
                }
            });
        }
        void CallBackTaskCB(AsyncTask task) {
            if(setHandle) {
                packQue.Enqueue(new AsyncTaskPack(task.tid, task.taskCB));
            }
            else {
                task.taskCB.Invoke(task.tid);
            }

            if(task.count == 0) {
                if(taskDic.TryRemove(task.tid, out AsyncTask temp)) {
                    LogFunc?.Invoke($"Task tid:{task.tid} run to completion.");
                }
                else {
                    ErrorFunc?.Invoke($"Remove tid:{task.tid} task in taskDic failed.");
                }
            }
        }

        protected override int GenerateTid() {
            // 使用原子操作优化性能，避免无限循环查找
            int newTid = Interlocked.Increment(ref tid);
            if(newTid == int.MaxValue || newTid < 0) {
                // 重置ID池
                lock(tidLock) {
                    if(tid >= int.MaxValue || tid < 0) {
                        tid = 0;
                    }
                    newTid = Interlocked.Increment(ref tid);
                }
            }

            // 如果ID已存在（极低概率），使用传统方法查找
            if(taskDic.ContainsKey(newTid)) {
                lock(tidLock) {
                    int startTid = newTid;
                    do {
                        newTid = Interlocked.Increment(ref tid);
                        if(newTid == int.MaxValue || newTid < 0) {
                            tid = 0;
                            newTid = Interlocked.Increment(ref tid);
                        }
                        if(!taskDic.ContainsKey(newTid)) {
                            return newTid;
                        }
                    } while(newTid != startTid); // 防止无限循环
                }
            }

            return newTid;
        }

        class AsyncTask {
            public int tid;
            public uint delay;
            public int count;

            public Action<int> taskCB;
            public Action<int> cancelCB;

            public DateTime startTime;
            public ulong loopIndex;
            public int fixDelta;

            public CancellationTokenSource cts;
            public CancellationToken ct;

            public AsyncTask(
                int tid,
                uint delay,
                int count,
                Action<int> taskCB,
                Action<int> cancelCB) {
                this.tid = tid;
                this.delay = delay;
                this.count = count;
                this.taskCB = taskCB;
                this.cancelCB = cancelCB;

                startTime = DateTime.UtcNow;

                this.loopIndex = 0;
                this.fixDelta = 0;
                cts = new CancellationTokenSource();
                ct = cts.Token;
            }
        }
    }
}


